module OmniVisionC3(
  CLK_50,
  LEDG,
  LEDR,
  BTN,
  //////////// Camera Interface /////////////////////////
  CAM_VSYNC,        // CAM V_SYNC
  CAM_HREF,         // CAM HREF
  CAM_PCLK,         // CAM PCLK
  CAM_DATA_Y_G,     // CAM Data lines Y/G
  CAM_RST,          // CAM Reset
  CAM_FODD,         // CAM FODD
  CAM_EXCLK,        // CAM on chip video osc. out
  CAM_SDA,          // CAM I2C interface data
  CAM_SCL,          // CAM I2C interface clock
  //////////////////// VGA  ////////////////////////////
  VGA_HS,           // VGA H_SYNC
  VGA_VS,           // VGA V_SYNC
  VGA_RGB,          // VGA RGB[3:0]
  //////////////////// UART  ////////////////////////////
  BT_RX,
  BT_TX,
  //////////////////// SPI  ////////////////////////////
  SPI_SCK,
  SPI_MISO,
  SPI_MOSI,
  SPI_CS
);

input  CLK_50;
input  [1:0] BTN;
output LEDG, LEDR;
//////////////////////// Camera interface //////////////////////
input  CAM_VSYNC;
input  CAM_HREF;
input  CAM_PCLK;
input  [7:0] CAM_DATA_Y_G;
output CAM_RST;
input  CAM_FODD;
input  CAM_EXCLK;
inout  CAM_SDA;
output CAM_SCL;
//////////////////////// VGA   ////////////////////////////
output VGA_HS;
output VGA_VS;
output [3:0] VGA_RGB;
//////////////////// UART  ////////////////////////////
output BT_TX;
input  BT_RX;
//////////////////// SPI  ////////////////////////////
output SPI_SCK;
input  SPI_MISO;
output SPI_MOSI;
output SPI_CS;

//---------------- Internal registers and wires --------------------------------------
wire resetN;
assign resetN = BTN[0];

wire camBusy;
wire [18:0] camWrAddress;
wire [7:0] camWrData;
wire camWrClk;
wire camEn = 'd1;
wire [9:0] camX,camY;

wire clk27;
wire clk25;

wire[5:0] crLightNum;
wire[95:0] crLight1, crLight2, crLight3;
wire crReady;
reg[9:0] crCenterX = 320, crCenterY = 240;

wire trigStart, trigBusy, trigError;
wire [1:0] trigFunc;
wire [31:0] trigData;
wire [31:0] trigResult;

wire [3:0] frameQ;
wire [14:0] frameRdAddress;
wire frameRdClk;

wire [18:0] vgaAddress;
wire vgaReadEn;
wire vgaClk   = clk25;
wire vgaRstN  = resetN;
wire vgaEn    = 1'b1;
wire [9:0] vgaX, vgaY;

wire uartBusy, uartRdClk;
wire [17:0] uartAddress;
wire [7:0] uartData;

wire spiBusy, spiRdClk, spiWrClk;
wire [17:0] spiAddress;
wire [7:0] spiData;
wire [7:0] spiInput;
reg spiValid = 0;

wire[4:0] uartMod, spiMod;
wire[95:0] uartDebug; 
wire[63:0] spiDebug;

//---------------- assigns ----------------------------------------------------
assign frameRdClk =  //uartBusy ? uartRdClk : 
                     //camBusy ? ~camWrClk :
                     ~(vgaReadEn & vgaClk);
        
assign frameRdAddress = //uartBusy ? uartAddress[14:0] :
                        //camBusy ? camY[9:2] * 160 + camX[9:2] :
                        vgaY[9:2] * 160 + vgaX[9:2];

assign LEDR = ~SPI_CS;
assign LEDG = trigError;

assign uartMod = (uartAddress - 6) % 12;  
assign uartDebug = ((uartAddress - 6) / 12 == 0) ? crLight1[95:0] :
                    ((uartAddress - 6) / 12 == 1) ? crLight2[95:0] : crLight3[95:0];                 
assign uartData =(uartAddress == 'd0 ? 8'h5a : 
                  uartAddress == 'd1 ? (crLightNum > 8'd3 ? 8'd3 : crLightNum)  : 
                  uartAddress == 'd2 ? crCenterX[7:0]  : 
                  uartAddress == 'd3 ? {6'd0, crCenterX[9:8]}  : 
                  uartAddress == 'd4 ? crCenterY[7:0]  : 
                  uartAddress == 'd5 ? {6'd0, crCenterY[9:8]} : 
                  uartMod == 'd0 ? uartDebug[7:0] :
                  uartMod == 'd1 ? uartDebug[15:8] :
                  uartMod == 'd2 ? uartDebug[23:16] :
                  uartMod == 'd3 ? uartDebug[31:24] :
                  uartMod == 'd4 ? uartDebug[39:32] :
                  uartMod == 'd5 ? uartDebug[47:40] :
                  uartMod == 'd6 ? uartDebug[55:48] :
                  uartMod == 'd7 ? uartDebug[63:56] :
                  uartMod == 'd8 ? uartDebug[71:64] :
                  uartMod == 'd9 ? uartDebug[79:72] :
                  uartMod == 'd10 ? uartDebug[87:80] : uartDebug[95:88]);    

                    
assign spiMod = (spiAddress - 2) % 8;
assign spiDebug = ((spiAddress - 2) / 8 == 0) ? crLight1[63:0] :
                  ((spiAddress - 2) / 8 == 1) ? crLight2[63:0] : crLight3[63:0];
assign spiData =  spiAddress == 'd0 ? 8'h5a : 
                  spiAddress == 'd1 ? (crLightNum > 8'd3 ? 8'd3 : crLightNum) : 
                  spiMod == 'd0 ? spiDebug[7:0] :
                  spiMod == 'd1 ? spiDebug[15:8] :
                  spiMod == 'd2 ? spiDebug[23:16] :
                  spiMod == 'd3 ? spiDebug[31:24] :
                  spiMod == 'd4 ? spiDebug[39:32] :
                  spiMod == 'd5 ? spiDebug[47:40] :
                  spiMod == 'd6 ? spiDebug[55:48] : spiDebug[63:56] ;      

//---------------- INPUT SIDE --------------------------------------

CcdCapture ccd(
  .iVsync(CAM_VSYNC),
  .iHref(CAM_HREF),
  .iPclk(CAM_PCLK),
  .iFodd(CAM_FODD),
  .iDataYG(CAM_DATA_Y_G),
  .oRst(CAM_RST),
  .oXCount(camX),
  .oYCount(camY),
  .oWrData(camWrData),
  .oWrAddress(camWrAddress),
  .oWrClk(camWrClk),
  .iCaptureEn(camEn),
  .oBusy(camBusy)
);

CameraConfig i2c(
  .iClk(CLK_50),
  .iRstN(resetN),
  .oSclk(CAM_SCL),
  .ioSda(CAM_SDA)
);

PLL_FAST pll27 (
  .inclk0(CLK_50),
  .c0(clk27)
);

VGA_PLL pll25 (
  .inclk0(clk27),
  .c0(clk25)
);

//---------------- FUNCTION ----------------------------------------
CamReader cr(
  .iPclk(camWrClk),
  .iClk(CLK_50),
  .iData(CAM_DATA_Y_G),
  //.iData({frameQ, 4'd0}),
  .iX(camX),
  .iY(camY),
  .iCenterX(crCenterX),
  .iCenterY(crCenterY),
  .iFrame(camBusy),
  .iRstN(resetN),
  .oTrigStart(trigStart),
  .oTrigFunc(trigFunc),
  .iTrigBusy(trigBusy),
  .iTrigError(trigError),
  .oTrigData(trigData),
  .iTrigResult(trigResult),
  .oLightNum(crLightNum),
  .oLight1(crLight1),
  .oLight2(crLight2),
  .oLight3(crLight3),
  .oReady(crReady)
);

TrigonometricFunc trig(
  .iRstN(resetN),
  .iClk(~CLK_50),
  .iStartProcess(trigStart),
  .iFunc(trigFunc),
  .oBusy(trigBusy),
  .oError(trigError),
  .iData(trigData),
  .oData(trigResult)
);

FrameRam fram(
  .data(CAM_DATA_Y_G[7:4]),
  .rdaddress(frameRdAddress),
  .rdclock(frameRdClk),
  .wraddress(camY[9:2] * 160 + camX[9:2] - 1),
  .wrclock(~camWrClk),
  .wren(camBusy),
  //.wren(0),
  .q(frameQ)
);

//---------------- DEBUG SIDE --------------------------------------

VgaController vga (
  .iRstN(vgaRstN),
  .iData(frameQ),
  .oAddress(vgaAddress),
  .oReadEn(vgaReadEn),
  .oVgaX(vgaX),
  .oVgaY(vgaY),
  .oVgaRgb(VGA_RGB),
  .oVgaHs(VGA_HS),
  .oVgaVs(VGA_VS),
  .iClk(vgaClk),
  .iEn(vgaEn)
);

UartController uart(
  .iClk(CLK_50),
  .iStartTransmit(crReady || ~BTN[1]),
  .iStartAddress(0),
  .iEndAddress(41),
  .oBusy(uartBusy),
  .oTx(BT_TX),
  .oMemAddress(uartAddress),
  .iMemData(uartData),
  .oMemClk(uartRdClk)
);

SpiController spi(
  .iClk(CLK_50),
  .iStartTransmit(crReady),
  .iStartAddress(0),
  .iEndAddress((crLightNum > 'd3 ? 'd3 : crLightNum) * 8 + 1),
  .oBusy(spiBusy),
  .oSclk(SPI_SCK),
  .iMiso(SPI_MISO),
  .oMosi(SPI_MOSI),
  .oCs(SPI_CS),
  .oMemAddress(spiAddress),
  .iMemData(spiData),
  .oMemData(spiInput),
  .oMemRdClk(spiRdClk),
  .oMemWrClk(spiWrClk)
);

//---------------- always block ----------------------------------------------------

always@(posedge spiWrClk) begin
  if(crLightNum > 'd0) begin
    case (spiAddress)
      2: begin
        spiValid <= spiInput[7];
        if(spiInput[7])
          crCenterX[9:8] <= spiInput[1:0];
      end
      3: if(spiValid) crCenterX[7:0] <= spiInput;
      4: if(spiValid) crCenterY[9:8] <= spiInput[1:0];
      5: if(spiValid) crCenterY[7:0] <= spiInput;
    endcase
  end
end

endmodule
